package com.myTest.factory;
import com.alibaba.druid.util.StringUtils;
import com.myTest.SpringConfig;
import com.myTest.annotation.*;

import java.beans.Introspector;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class BeanFactory {
    //外部配置文件配置信息
    private static Properties properties = new Properties();
    //存放处理bean的基本信息
    private static Map<String,Class<?>> beanDefinitionMap = new HashMap<String,Class<?>>();
    //扫描到的所有类
    private static List<Class<?>> classList = new ArrayList<Class<?>>();

    //缓存bean 容器缓存（单例缓存池）
    private static ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<String, Object>();

    static {
        try {
            //1、加载SpringConfig启动类，得到扫描包路径
            Class<?> configClass = SpringConfig.class;
            //使用内省Introspector的decapitalize方法将类名首字母变小写，得到springConfig
            String configBeanName = Introspector.decapitalize(configClass.getSimpleName());
            beanDefinitionMap.put(configBeanName,configClass);
            //value的值com：.myTest；getAnnotation方法是获取注解类
            String[] value = configClass.getAnnotation(ComponentScan.class).value();
            //2、加载包下的所有类
            if(value.length>0){
                for (String s : value) {
                    scanPackages(s,classList);
                }
            }else {
                scanPackages("",classList);
            }

            //3、遍历所有类,放入beanDefinitionMap集合中,通过内省获得key
            if(classList.size()>0){
                for (Class<?> aClass : classList) {
                    String keyName = Introspector.decapitalize(aClass.getSimpleName());
                    //判断类是否加载@Component注解  @Component("connectionUtils")
                    if(aClass.isAnnotationPresent(Component.class)){
                        Component component = aClass.getAnnotation(Component.class);
                        if(!StringUtils.isEmpty(component.value())){
                            keyName = component.value();
                        }else if (aClass.getInterfaces().length > 0) {
                            keyName = Introspector.decapitalize(aClass.getInterfaces()[0].getSimpleName());
                        }
                    }
                    //判断类是否加载@Service注解
                    else if(aClass.isAnnotationPresent(Service.class)){
                        Service service = aClass.getAnnotation(Service.class);
                        if(!StringUtils.isEmpty(service.value())){
                            keyName = service.value();
                        }else if (aClass.getInterfaces().length > 0) {
                            keyName = Introspector.decapitalize(aClass.getInterfaces()[0].getSimpleName());
                        }
                    }
                    //判断类是否加载@Repository注解
                    else if(aClass.isAnnotationPresent(Repository.class)) {
                        Repository annotation = aClass.getAnnotation(Repository.class);
                        if (!annotation.value().equals("")) {
                            keyName = annotation.value();
                        }
                    }else{
                        continue;
                    }
                    beanDefinitionMap.put(keyName, aClass);
                }
            }
            //4、加载数据库连接信息
            properties = getProperties("jdbc.properties");
            //5、实例化并初始化所有bean
            getBean(configBeanName);
            for (String beanName : beanDefinitionMap.keySet()) {
                getBean(beanName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取bean
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean == null) {
            bean = doCreateBean(beanName);
        }
        return bean;
    }

    /**
     * 创建bean并初始化
     * 1、实例化Bean
     * 2、初始化Bean
     * @param beanName
     * @throws Exception
     */
    private static Object doCreateBean(String beanName) throws Exception {
        //实例化创建对象
        createBeanInstance(beanName);
        //初始化对象
        populateBean(beanName);
        return beanMap.get(beanName);
    }

    /**
     * 实例化Bean
     *
     * @param beanName
     * @throws Exception
     */
    private static void createBeanInstance(String beanName) throws Exception {
        Class<?> clazz = beanDefinitionMap.get(beanName);
        //实例化对象方法
        Object bean = clazz.newInstance();
        beanMap.put(beanName, bean);
    }

    /**
     * 初始化Bean
     * 解析 @Autowired、@Value、@Transactional等注解
     *
     * @param beanName
     */
    private static void populateBean(String beanName) throws Exception {
        Object bean = getBean(beanName);
        Class<? extends Object> clazz = bean.getClass();
        //1、依赖注入
        //getDeclaredFields()获取所有Class上的字段
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            //getAnnotations()获取字段上的所有注解
            Annotation[] annotations = field.getAnnotations();
            for (Annotation annotation : annotations) {
                //判断字段上的注解类是否是Autowired.class
                if (annotation.annotationType().equals(Autowired.class)) {
                    //通过属性名称 获取bean
                    Object value = getBean(field.getName());
                    //打开反射设置执行开关
                    field.setAccessible(true);
                    //给属性赋值
                    field.set(bean, value);
                }
                //判断字段上的注解类是否是Value.class
                else if (annotation.annotationType().equals(Value.class)) {
                    Value valueAnno = field.getAnnotation(Value.class);
                    String value = valueAnno.value();
                    if (value.startsWith("${") && value.endsWith("}")) {
                        value = properties.getProperty(value.split("\\$\\{")[1].split("}")[0]);
                    }
                    //打开反射设置执行开关
                    field.setAccessible(true);
                    //给属性赋值
                    field.set(bean, value);
                }
            }
        }

        //2方法注解处理
        //getMethods()获取所有Class上的方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            //getAnnotations()获得方法上的所有注解类
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                //判断方法上的注解类是否是Transactional.class
                if (annotation.annotationType().equals(Transactional.class)) {
                    //转为代理对象
                    ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
                    if (clazz.getInterfaces().length > 0) {
                        //JDK代理对象替换原对象，增加事务控制类
                        bean = proxyFactory.getJdkProxy(bean);
                    } else {
                        //Cglib代理对象替换原对象
                        bean = proxyFactory.getCglibProxy(bean);
                    }
                    beanMap.put(beanName, bean);
                }
                //判断方法上的注解类是否是Bean.class
                else if (annotation.annotationType().equals(Bean.class)) {
                    Bean beanAnno = method.getAnnotation(Bean.class);
                    //getReturnType()获取方法的返回类型
                    Class<?> returnClass = method.getReturnType();
                    String beanName1 = Introspector.decapitalize(returnClass.getSimpleName());
                    if (!beanAnno.value().equals("")) {
                        beanName1 = beanAnno.value();
                    }
                    //获取方法执行返回的对象，返回的就是bean
                    Object beanClass = method.invoke(bean);
                    beanMap.put(beanName1, beanClass);
                }
            }
        }
    }


    //读取配置文件
    private static Properties getProperties(String... fileNames) {
        Properties properties = new Properties();
        try {
            if (fileNames.length > 0) {
                for (String fileName : fileNames) {
                    InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream(fileName);
                    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
                    properties.load(inputStreamReader);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(" read properties is fail!");
        }
        return properties;
    }


    /**
     * 获取包路径下的所有类
     *
     * @param packageName
     * @param classes
     */
    private static void scanPackages(String packageName,List<Class<?>> classes){
        try {
            //将包名转换为路径
            String packagesUrl=BeanFactory.class.getResource("/"+packageName.replaceAll("\\.", "/")).getFile();
            File scanFile=new File(packagesUrl);
            String[] filenameList = scanFile.list();
            for (String filename : filenameList) {
                File file=new File(scanFile,filename);
                //判断当前file表示的是一个目录则返回true
                if(file.isDirectory()){
                    scanPackages(packageName+"."+filename,classes);
                    // System.out.println("当前进入的是dir："+file);
                }else{
                    if(file.isFile()){
                        //String VAL = packageName+"."+file.getName().split("\\.")[0];
                        // System.out.println(VAL);
                        Class<?> aClass = Class.forName(packageName+"."+filename.replace(".class",""));
                        //Class<?> aClass = Class.forName(packageName+"."+file.getName().split("\\.")[0]);
                        classes.add(aClass);
                        //System.out.println("当前file 是：" + file);
                        // System.out.println(packageName+"."+filename.replace(".class",""));
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws ClassNotFoundException {
        // BeanFactoryAnno beanFactoryNew = new BeanFactoryAnno();
        Class<?> newClass = Class.forName("com.myTest.servlet.TransferServlet");
        System.out.println(12);

    }
}
